/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.cezary.butler.walili.server.db.tasks;

import java.sql.SQLException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Przemysław Bałdyga
 */
public abstract class FetchingTask<R> extends DbTask {

    private static final AtomicInteger tasksDone = new AtomicInteger();
    private static final AtomicInteger tasksDoneWithTimeWarn = new AtomicInteger();
    
    private static final int LONG_WAIT_WARN_TIME = 1000;
    private static final Logger log = Logger.getLogger(FetchingTask.class.getName());
    private final Semaphore sem;

    public FetchingTask() {
        sem = new Semaphore(1);
        sem.acquireUninterruptibly();
    }

    protected final void join() {
        log.finest("Waiting for task result");
        long current = System.currentTimeMillis();
        try {
            sem.acquire();
            final long waitt = System.currentTimeMillis() - current;
            final boolean time_warn = waitt > LONG_WAIT_WARN_TIME;
            if(time_warn)tasksDoneWithTimeWarn.incrementAndGet();
            final Level level = (time_warn) ? Level.WARNING : Level.FINE;            
            if (log.isLoggable(level)) {
                log.log(level, "Received ({1}) task result in {0} miliseconds {2}/{3}", new Object[]{waitt,this,tasksDoneWithTimeWarn,tasksDone});
            }
        } catch (InterruptedException ex) {
            final DbTaskInterrupted dbTaskInterrupted = new DbTaskInterrupted(String.format("Task %s was interrupted", toString()), ex);
            log.throwing(FetchingTask.class.getName(), "join", dbTaskInterrupted);
            throw dbTaskInterrupted;

        } finally {
            sem.release();
        }        
    }

    protected abstract void executeInternal() throws SQLException;

    @Override
    public final void execute() throws SQLException {
        try {
            executeInternal();
        } finally {
            wykonano();
        }
    }

    protected final void wykonano() {
        if (sem.availablePermits() != 0) {
            log.log(Level.SEVERE, "Semaphore is being released but available permits do not add up.");
        }
        sem.release();
        tasksDone.incrementAndGet();
        log.finest("Lock released");

    }

    protected abstract R $getResultsNonLocking();

    public R getResults() {
        join();
        return $getResultsNonLocking();
    }
}
